package com.he.config;

import com.he.filter.JwtAuthenticationTokenFilter;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.List;

/**
 * @author He
 * @version 1.0
 * @Date 2023/9/6 16:12
 * @Desc SpringSecurity配置类
 */
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {


    /**
     * 接口文档放行
     */
    public static final List<String> DOC_WHITE_LIST = List.of("/login","/",
            "/article/**", "/recent/comment", "/message/list", "/message/wallpaper", "/archives/list",
            "/tag/article/list", "/admin/article/edit/**", "/category/list", "/message/wallpaper",
            "/photo", "/tag/list", "/link/friend", "/comment/list",
            "/task/list","/sensor/getSensorCount",
            "/sensor/getSensorHistory","/swagger-ui.html/**",
            "/user/logout","/user/login","/mqtt/send",
            "/doc.html", "/webjars/**", "/v3/api-docs/**", "/api/websocket/**");


    /**
     * 注入自定义的UserDetailsService，这个类是SpringSecurity用于根据用户名查询用户信息的接口，
     *  在调用authenticationManager.authenticate()
     *   authenticate.getPrincipal();获取到的就是这个接口的实现类，springsecurity6.0之后不再支持直接使用UserDetailsService
     * */
    @Qualifier("userDetailServiceImp")
    @Resource
    private UserDetailsService userDetailService;

    /**
     * 将AuthenticationManager（认证管理器） 注入到Spring中在SpringSecurity6中我们必须手动注入AuthenticationManager同时注入UserDetailsService
     * 通过DaoAuthenticationProvider将UserDetailsService注入到AuthenticationManager中
     * 通过ProviderManager将DaoAuthenticationProvider注入到AuthenticationManager中
     */
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(daoAuthenticationProvider);
    }


    /**
     * 配置加密规则
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 注入jwt过滤器
     */
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 注入认证失败处理器
     */
    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;

    /**
     * 注入授权失败处理器
     */
    @Resource
    private AccessDeniedHandler accessDeniedHandler;


    /**
     * 配置安全策略
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http    // 禁用basic明文验证
                .httpBasic(AbstractHttpConfigurer::disable)
                // 前后端分离架构不需要csrf保护
                .csrf(AbstractHttpConfigurer::disable)
                // 禁用默认登录页
                .formLogin(AbstractHttpConfigurer::disable)
                // 禁用默认登出页
                .logout(AbstractHttpConfigurer::disable)
                // 前后端分离是无状态的，不需要session了，直接禁用。
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeRequests((authorize) -> authorize
                        ///静态资源，可匿名访问
                        .requestMatchers(DOC_WHITE_LIST.toArray(new String[0])).permitAll()
                        .anyRequest().authenticated())
                .exceptionHandling( exceptionHandlingConfigurer -> exceptionHandlingConfigurer
                        .authenticationEntryPoint(authenticationEntryPoint)
                        .accessDeniedHandler(accessDeniedHandler))
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}
